package com.jy.datapipeline.export.service.impl;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSONObject;
import com.jy.datapipeline.common.config.DBMetaDataConfig;
import com.jy.datapipeline.export.dao.DataSourceInfoDao;
import com.jy.datapipeline.export.dao.repo.DataSourceInfoDaoRepo;
import com.jy.datapipeline.export.entity.DataSourceInfoModel;
import com.jy.datapipeline.export.service.DataSourceInfoService;
import com.jy.datapipeline.importserver.common.dto.ImportDataSourceNamesDto;
import lombok.extern.slf4j.Slf4j;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j

@Transactional
@Service
public class DataSourceInfoServiceImpl implements DataSourceInfoService {
    @Autowired
    private DataSourceInfoDao dataSourceInfoDao;

    @Autowired
    private DataSourceInfoDaoRepo dataSourceInfoDaoRepo;

    private static final ExpiringMap<String,List<String>> expmap1 = ExpiringMap.builder()
            .maxSize(20420)
            .expiration(1200, TimeUnit.SECONDS)
            .expirationPolicy(ExpirationPolicy.ACCESSED)
            .variableExpiration()
            .build();

    @Override
    public boolean saveDataSourceInfo(DataSourceInfoModel dsim) {
        this.dataSourceInfoDaoRepo.save(dsim);
        return true;
    }

    @Override
    public boolean updateDataSourceInfo(DataSourceInfoModel dsim) {
        return this.dataSourceInfoDao.updateDataSourceInfo(dsim);
    }

    @Override
    public boolean deleteDataSourceInfoById(int id) {
        this.dataSourceInfoDaoRepo.deleteById(id);
        return true;
    }

    @Override
    public List<Map<String, Object>> findDataSourceInfoByList(String dataSourceName,
        int iDisplayStart, int iDisplayLength) {
        List<Map<String, Object>> dataSourceInfoByList = this.dataSourceInfoDao.findDataSourceInfoByList(dataSourceName,
                iDisplayStart, iDisplayLength);
        return this.toUpperCaseDatasource(dataSourceInfoByList);
    }

    private List<Map<String, Object>> toUpperCaseDatasource(List<Map<String, Object>> dataSourceInfoByList) {
        for (Map<String, Object> dataSourceInfo : dataSourceInfoByList) {
            dataSourceInfo.put("data_source_name", ((String) dataSourceInfo.get("data_source_name")).toUpperCase(Locale.ROOT));
        }
        return dataSourceInfoByList;
    }

    @Override
    public int findDataSourceInfoByCount(String dataSourceName) {
        return this.dataSourceInfoDao.findDataSourceInfoByCount(dataSourceName);
    }

    @Override
    public DataSourceInfoModel findByDataSourceName(String dataSourceName) {
        return this.dataSourceInfoDaoRepo.findByDataSourceName(dataSourceName);
    }

    @Override
    public DataSourceInfoModel findByDataSourceId(String dataSourceId) {
        return this.dataSourceInfoDaoRepo.findByDataSourceId(dataSourceId);
    }

    @Override
    public List<DataSourceInfoModel> findAll() {
        Iterable<DataSourceInfoModel> iterable=  this.dataSourceInfoDaoRepo.findAll();
        List<DataSourceInfoModel> list=new ArrayList<>();
        iterable.forEach(dsif->{
            list.add(dsif);
        });
        return list;
    }

    @Override
    public DataSourceInfoModel findById(int id) {
        Optional<DataSourceInfoModel> op=  this.dataSourceInfoDaoRepo.findById(id);
        return op.get();
    }

    @Override
    public Map<String, Object> findTables(String dataSourceId, String schema,
                                          String tblName, int searchType,
                                          int startIndex, Integer endIndex,
                                          Map<String, Object> map) throws SQLException {
        DruidDataSource ds= DBMetaDataConfig.dataSourceMap.get(dataSourceId);
        List<String> filterlist = expmap1.get(schema);
        List<String> resultlist=new ArrayList<>();
        if(filterlist==null || filterlist.size()<=0){
            filterlist=this.getAllTables(ds,schema,"");
            expmap1.put(schema, filterlist);
        }

        if(filterlist.size()>0) {
            List<String> templist = new ArrayList<>();
            if (tblName != null && !tblName.equals("")) {
                for (String str : filterlist) {
                    //模糊匹配
                    if (searchType == 0) {
                        if (str.contains(tblName) || (str.toLowerCase().contains(tblName)
                                || str.toUpperCase().contains(tblName))) {
                            //将筛选的值存入到list中
                            templist.add(str);
                        }
                    } else {
                        if (str.equals(tblName) || (str.toLowerCase().equals(tblName)
                                || str.toUpperCase().equals(tblName))) {
                            //将筛选的值存入到list中
                            templist.add(str);
                        }
                    }
                }
            }

            if (endIndex == null || endIndex == 0) {
                endIndex = filterlist.size();
            }

            if (startIndex >= 0 && startIndex < endIndex) {
                if (tblName != null && !tblName.equals("") && templist.size() > 0) {
                    for (int i = (startIndex > templist.size() ? 0 : startIndex);
                         i < (Math.min(endIndex, templist.size())); i++) {
                        resultlist.add(templist.get(i));
                    }
                    map.put("ruleTblList", resultlist);
                    map.put("ruleTblCount", resultlist.size());
                    map.put("status", true);
                    map.put("message", "success");
                } else {
                    if(searchType==0){
                        for (int i = (startIndex > filterlist.size() ? 0 : startIndex);
                             i < (Math.min(endIndex, filterlist.size())); i++) {
                            resultlist.add(filterlist.get(i));
                        }
                        map.put("ruleTblList", resultlist);
                        map.put("ruleTblCount", filterlist.size());
                        map.put("status", true);
                        map.put("message", "success");
                    }else{
                        map.put("ruleTblList", resultlist);
                        map.put("ruleTblCount", filterlist.size());
                        map.put("status", true);
                        map.put("message", "success");
                    }

                }
            }
        }
        map.put("status", true);

        return this.toUpperCaseTables(map);
    }

    /**
     * 表名转大写
     * @param map
     * @return
     */
    private Map<String, Object> toUpperCaseTables(Map<String, Object> map) {
        List<String> ruleTblList = JSONObject.parseArray(JSONObject.toJSONString(map.get("ruleTblList")), String.class);
        List<String> ruleTblUpperCase = new ArrayList<>();
        for (String ruleTbl : ruleTblList) {
            ruleTblUpperCase.add(ruleTbl.toUpperCase(Locale.ROOT));
        }
        map.put("ruleTblList", ruleTblUpperCase);
        return map;
    }

    public List<String> getAllTables(DruidDataSource dataSource,String schema,String tableName) throws SQLException {
        try (Connection connection = dataSource.getConnection()) {
            connection.setAutoCommit(false);
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getTables(null, schema, tableName==""?null:tableName, new String[] { "TABLE" });
            rs.setFetchSize(100);
            ArrayList<String> list = new ArrayList<>(20);
            while (rs.next()) {
                String table= rs.getString("TABLE_NAME");

                list.add(table);
            }
            rs.close();
            connection.commit();
            connection.close();
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }
}
